package controller

import (
	"Short_chain_cats/config"
	"Short_chain_cats/shortlink/model"
	"Short_chain_cats/shortlink/service"
	"Short_chain_cats/util"
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"io/ioutil"
	"log"
	"math/bits"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

type linksController struct {
	*service.LinksService
}

var LinksController = linksController{
	LinksService: service.NewLinksService(),
}

const (
	c1           = 0xcc9e2d51
	c2           = 0x1b873593
	r1           = 15
	r2           = 13
	m            = 5
	n            = 0xe6546b64
	base62Chars1 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)

// ClickCounter 用于存储短链和对应的点击次数
type ClickCounter struct {
	sync.Mutex
	Counts map[string]int
}

// NewClickCounter 初始化 ClickCounter 结构体
func NewClickCounter() *ClickCounter {
	return &ClickCounter{
		Counts: make(map[string]int),
	}
}

// Generate 生成短链接口
func (r linksController) Generate(ctx *gin.Context) {
	var links model.GenerateLinksReq
	err := ctx.ShouldBindJSON(&links)
	if err != nil {
		util.RespFailed(ctx, util.ParamError, util.Translate(err))
		return
	}
	//获取userid
	userIDAny, _ := ctx.Get("userID")
	// 尝试将 userID 断言为 int 类型
	userID, ok := userIDAny.(int)
	if !ok {
		// 如果断言失败，返回错误
		util.RespFailed(ctx, util.VerificationFailed, "token校验失败")
		return
	}

	//绑定参数
	links.UserId = userID
	fmt.Println("获取到的userid = ", links.UserId)
	//校验URL的基本格式是否合法
	match, err := validateURL(links.LongUrl)
	if err != nil {
		util.RespFailed(ctx, util.Failed, util.Translate(err))
		return
	}
	if match == false {
		util.RespFailed(ctx, util.ParamError, util.Translate(err))
		return
	}
	//判断该长链之前是否存在过
	isAlive, err := r.Check(ctx, links.LongUrl)
	if err != nil {
		util.RespFailed(ctx, util.Failed, util.Translate(err))
		return
	}
	//长链已经注册过
	if isAlive != nil {
		util.RespSuccess(ctx, "生成短链成功！", isAlive)
		return
	}
	//如果该长链没有注册过,则进行操作
	//截取需要转换的部分,prefix是前缀，str是剩余部分
	//str := intercept(links.LongUrl)
	//转换为[]byte类型
	arrayOfString := []byte(links.LongUrl)
	resultTen := MurmurHash3(arrayOfString, 0)
	//转换成62进制
	result := int64ToBase62(int64(resultTen))

	//获取标题
	title, image := getTitle(links.LongUrl)

	//组合所有的信息
	linksStor := model.GenerateLinksResp{}
	links.GenerateLinksResponse(&linksStor)
	linksStor.SortUrl = result
	linksStor.Title = title
	linksStor.UrlImg = image

	//存入数据库
	res, err := r.LinksService.Create(ctx, &linksStor)
	if err != nil {
		util.RespFailed(ctx, util.Failed, err.Error())
	}
	util.RespSuccess(ctx, "生成短链成功！", res)
}

// View 查看所有我的短链
func (r linksController) View(ctx *gin.Context) {
	//绑定参数
	var req model.ViewLinksReq
	err := ctx.Bind(&req)
	if req.Page < 1 || req.PageSize < 1 {
		util.RespFailed(ctx, util.ParamError, "参数应为非负数！")
		return
	}
	req.Page = req.Page - 1
	//进行登录校验，拿到userId
	userIDAny, _ := ctx.Get("userID")
	// 尝试将 userID 断言为 int 类型
	userID, ok := userIDAny.(int)
	if !ok {
		// 如果断言失败，返回错误
		util.RespFailed(ctx, util.VerificationFailed, "token校验失败")
		return
	}
	req.UserId = int64(userID)
	res, err := r.LinksService.View(ctx, req)
	if err != nil {
		util.RespFailed(ctx, util.Failed, err.Error())
		return
	}
	if res == nil {
		util.RespSuccess(ctx, "未查找到数据", "")
		return
	}
	util.RespSuccess(ctx, "查找成功！", res)
	return
}

// Visit 短链访问长链
func (r linksController) Visit(ctx *gin.Context) {
	//解析短链部分
	shortLink := ctx.Param("shortLink")
	host := ctx.Request.Host
	fmt.Println("获取到的host为：", host)
	//从数据库中查找相关长链
	res, err := r.LinksService.Visit(ctx, shortLink)
	if err != nil {
		util.RespFailed(ctx, util.Failed, err.Error())
		return
	}
	if res == nil {
		util.RespSuccess(ctx, "未查找到数据", "")
		return
	}
	//判断短链的状态
	if res.Status != 1 {
		util.RespFailed(ctx, util.NotFound, "链接不存在！")
		return
	}
	//判断是否到了点击次数
	//如果查找到了该数据，就进行一个重定向
	util.RespRedirect(ctx, res.LongUrl)
	return
}

// Publish 发布短链
func (r linksController) Publish(ctx *gin.Context) {
	var req model.ReviseLinksReq
	var links model.Links
	var tags string

	err := ctx.ShouldBindJSON(&req)

	if err != nil {
		util.RespFailed(ctx, util.ParamError, util.Translate(err))
		return
	}
	//获取userid
	userIDAny, _ := ctx.Get("userID")
	// 尝试将 userID 断言为 int 类型
	userID, ok := userIDAny.(int)
	if !ok {
		// 如果断言失败，返回错误
		util.RespFailed(ctx, util.VerificationFailed, "token校验失败")
		return
	}

	//处理标签字段
	if len(req.Tags) > 0 {
		var mid strings.Builder
		l := len(req.Tags) - 1
		for i := 0; i < l; i++ {
			mid.WriteString(strconv.Itoa(req.Tags[i]))
			mid.WriteString("#")
		}
		mid.WriteString(strconv.Itoa(req.Tags[l]))
		tags = mid.String()
	}

	//处理参数
	req.GenerateLinksReq(&links)
	links.UserId = userID
	links.Tags = tags
	res, err := r.LinksService.Publish(ctx, &links)
	if err != nil {
		util.RespFailed(ctx, util.Failed, err.Error())
		return
	}
	util.RespSuccess(ctx, "修改成功！", res)
	return
}

// Remove 删除短链
func (r linksController) Remove(ctx *gin.Context) {
	id := ctx.Param("linksId")
	num, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		util.RespFailed(ctx, util.Failed, err.Error())
		return
	}
	err = r.LinksService.Remove(ctx, num)
	if err != nil {
		util.RespFailed(ctx, util.Failed, err.Error())
		return
	}
	util.RespSuccess(ctx, "删除成功！", "")
	return
}

// Restore 还原短链
func (r linksController) Restore(ctx *gin.Context) {

	var Links struct {
		ShortLink string `json:"shortLink" `
	}
	err := ctx.ShouldBindJSON(&Links)
	l := len(config.Prefix)
	if len(Links.ShortLink) < l || len(Links.ShortLink) == l {
		util.RespFailed(ctx, util.ParamError, err.Error())
		return
	}

	Links.ShortLink = Links.ShortLink[l:]
	//从数据库中查找相关长链
	res, err := r.LinksService.Visit(ctx, Links.ShortLink)
	if err != nil {
		util.RespFailed(ctx, util.Failed, err.Error())
		return
	}
	if res == nil {
		util.RespFailed(ctx, util.Failed, "未查找到数据")
		return
	}
	util.RespSuccess(ctx, "还原成功！", res)
	return
}

// Edit 修改短链
func (r linksController) Edit(ctx *gin.Context) {

	var req model.EditLinksReq
	var links model.EditLinksModel
	var tags string
	err := ctx.ShouldBindJSON(&req)
	if err != nil {
		util.RespFailed(ctx, util.ParamError, util.Translate(err))
		return
	}

	//获取userid
	userIDAny, _ := ctx.Get("userID")
	// 尝试将 userID 断言为 int 类型
	userID, ok := userIDAny.(int)
	if !ok {
		// 如果断言失败，返回错误
		util.RespFailed(ctx, util.VerificationFailed, "token校验失败")
		return
	}

	//判断状态
	if req.PrivateTarget == 2 && req.Password == "" {
		util.RespFailed(ctx, util.ParamError, "请输入密码")
		return
	}
	req.UserId = userID
	//处理标签字段
	if len(req.Tags) > 0 {
		var mid strings.Builder
		l := len(req.Tags) - 1
		for i := 0; i < l; i++ {
			mid.WriteString(strconv.Itoa(req.Tags[i]))
			mid.WriteString("#")
		}
		mid.WriteString(strconv.Itoa(req.Tags[l]))
		tags = mid.String()
		fmt.Println(tags)
	}

	//填充数据
	req.GenerateEditLinksReq(&links)
	links.UserId = userID
	links.Tags = tags
	fmt.Println("获取到的标签为：", links.Tags)

	err = r.LinksService.Edit(ctx, &links)
	if err != nil {
		util.RespFailed(ctx, util.Failed, err.Error())
		return
	}
	util.RespSuccess(ctx, "编辑成功！", nil)
	return
}

// 校验URL的基本格式是否合法
func validateURL(url string) (bool, error) {
	regex := `^(http|https)://[a-zA-Z0-9-.]+.[a-zA-Z]{2,3}(/[^/]+)*/?$`
	match, err := regexp.MatchString(regex, url)
	if err != nil {
		return false, err
	}
	return match, nil
}

// intercept 截取需要转换的部分
func intercept(s string) string {
	count := 0
	num := 0
	for i := range s {
		if s[i] == '/' {
			count++
		}
		if count == 3 {
			num = i
			break
		}
	}
	//prefix := s[0 : num+1]
	str := s[num+1:]

	return str
}

// MurmurHash3 算法进行计算
func MurmurHash3(key []byte, seed uint32) uint32 {
	var h1 = seed
	var k1 uint32
	var chunk uint32

	blocks := len(key) / 4

	for i := 0; i < blocks; i++ {
		chunk = uint32(key[i*4]) | uint32(key[i*4+1])<<8 | uint32(key[i*4+2])<<16 | uint32(key[i*4+3])<<24

		k1 = chunk
		k1 *= c1
		k1 = bits.RotateLeft32(k1, r1)
		k1 *= c2

		h1 ^= k1
		h1 = bits.RotateLeft32(h1, r2)
		h1 = h1*m + n
	}

	tail := key[blocks*4:]
	switch len(tail) {
	case 3:
		h1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		h1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		h1 ^= uint32(tail[0])
		h1 *= c1
		h1 = bits.RotateLeft32(h1, r1)
		h1 *= c2
	}

	h1 ^= uint32(len(key))

	h1 ^= h1 >> 16
	h1 *= 0x85ebca6b
	h1 ^= h1 >> 13
	h1 *= 0xc2b2ae35
	h1 ^= h1 >> 16

	return h1
}

// int64ToBase62 将int64类型转换为62进制的字符串表示
func int64ToBase62(num int64) string {
	if num == 0 {
		return "0"
	}

	var result strings.Builder
	base := int64(62)

	isNegative := false
	if num < 0 {
		isNegative = true
		num = -num
	}

	for num > 0 {
		remainder := num % base
		result.WriteByte(base62Chars1[remainder])
		num = num / base
	}

	resultStr := result.String()
	resultStr = reverseString(resultStr)

	if isNegative {
		resultStr = "-" + resultStr
	}

	return resultStr
}

func reverseString(s string) string {
	runes := []rune(s)
	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
		runes[i], runes[j] = runes[j], runes[i]
	}
	return string(runes)
}

// 获取标题
func getTitle(url string) (string, string) {
	var title string
	var picture string
	// 发送HTTP GET请求，并获取网页响应
	response, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	//这里判断请求被拦截的情况
	if response.StatusCode == 403 {
		title = ""
		picture = "http://sems1.oss-cn-beijing.aliyuncs.com/logo.png"
		return title, picture
	}
	defer func(Body io.ReadCloser) {
		err = Body.Close()
		if err != nil {
			return
		}
	}(response.Body)

	// 读取网页内容
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal(err)
		return "", ""
	}

	// 定义正则表达式
	ti := regexp.MustCompile(`<title>(.*?)</title>`)

	// 使用正则表达式匹配标题
	matches1 := ti.FindStringSubmatch(string(body))

	if len(matches1) > 1 {
		title = matches1[1]
		// 打印网页标题
		fmt.Println("获取到的标题是：", title)
	}
	// 定义获取图片的正则表达式
	pi := regexp.MustCompile(`<link\s+[^>]*?rel=["'](?:icon|shortcut icon)["'][^>]*?href=["']([^"']+)["']`)

	// 使用正则表达式匹配图片
	matches2 := pi.FindStringSubmatch(string(body))

	if len(matches2) > 1 {
		picture = matches2[1]
		// 打印网页标题
		fmt.Println("获取到的图片是：", picture)
	} else {
		fmt.Println("未获取到图片")
	}
	return title, picture
}
